package com.cskaoyan.javase.object._3hashcode;

/**
 * @description: hashCode方法
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

/**
 * hashCode方法声明:
 *      public native int hashCode();
 *      该方法是一个本地方法,不是Java代码实现的,注意它的返回值,是一个int整数
 * hashCode方法的作用:
 *      1.返回该对象的哈希码值(该方法的返回值就是哈希码值,也叫哈希值)
 *      2.支持此方法是为了提高哈希表（例如 java.util.Hashtable 提供的哈希表）的性能。
 *          哈希表简单理解就是key-value(键值对)
 *          通过hashCode方法计算某个对象的哈希值,然后将它作为作为哈希表的key
 *
 * hashCode方法的实现:
 *      1.Object类当中的默认实现:
 *          实际上，由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。
 *         （这一般是通过将该对象的内部地址转换成一个整数来实现的，但是 JavaTM 编程语言不需要这种实现技巧。）
 *          Object类当中的hashCode方法默认实现,是把对象的地址值转换成了一个整数
 *          一般情况下,只有是同一个对象,它们的哈希值才相等
 *          所以一般把hashCode方法返回值哈希值当成对象的地址值(近似)
 *
 *      2.很多时候我们不希望通过地址值去计算哈希值,所以需要重写该方法
 *          重写hashCode方法的常规协定:
 *              a.在 Java 应用程序执行期间，在对同一对象多次调用 hashCode 方法时，必须一致地返回相同的整数。
 *                  前提是将对象进行 equals 比较时所用的信息没有被修改
 *                  从某一应用程序的一次执行到同一应用程序的另一次执行，该整数无需保持一致
 *             重点: hashCode方法的重写应该参考equals方法,都使用成员变量的取值去重写
 *                  如果重写就一起重写,否则就都不重写
 *
 *              b.如果根据 equals(Object) 方法，两个对象是相等的
 *                  那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
 *             说明: equals方法的优先级更高,优先保证equals方法的准确性,这一点是必须要保证的
 *
 *              c.如果根据 equals(java.lang.Object) 方法，两个对象不相等
 *                  那么对这两个对象中的任一对象上调用 hashCode 方法不要求一定生成不同的整数结果
 *              说明: 原因是存在哈希冲突,所以没办法强制要求这一点
 *                  但是，程序员应该意识到，为不相等的对象生成不同整数结果可以提高哈希表的性能。
 *                  虽然哈希冲突不可能被解决,但是我们要相信光,追求更好的哈希算法,解决哈希冲突对程序是有利的
 *
 *       3.哈希方法的重写,实际上是设计哈希算法,一般情况下,使用idea自动生成的即可
 *       4.注意事项:
 *           a.如果成员变量中有引用数据类型,那就去调用它的hashCode方法,根据需要重写它的hashCode方法
 *              重写的规则要和equals方法保持一致
 *           b.重写后的hashCode方法就不再是地址值了,再打印对象名得到的就不是地址值了
 *
 */
public class Demo {
    public static void main(String[] args) {

        Student s1 = new Student(18,"张三",100);
        Student s2 = new Student(18,"张三",100);
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s1 == s2);

    }
}

class Student{
    int age;
    String name;
    double score;

    public Student() {
    }

    public Student(int age, String name, double score) {
        this.age = age;
        this.name = name;
        this.score = score;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        if (Double.compare(student.score, score) != 0) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = age;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        temp = Double.doubleToLongBits(score);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}